package db

import (
	"context"
	"fmt"
	"goProject/cfg"
	"os"
	"runtime"
	"time"

	"gitee.com/ruige_fun/util/rlog"

	"gitee.com/ruige_fun/util/rtool"

	"gitee.com/ruige_fun/util/rwheel"

	"gitee.com/ruige_fun/util/std"
	"github.com/shirou/gopsutil/v3/disk"
	"github.com/shirou/gopsutil/v3/mem"

	"gitee.com/ruige_fun/util/rdb"
	"github.com/redis/go-redis/v9"

	"gorm.io/gorm"
)

var MySQL *gorm.DB     //MySQL客户端
var MySQLData *gorm.DB //MySQL客户端

var Redis *redis.Client      //Redis客户端
var RedisToken *redis.Client //Redis客户端
var RedisData *redis.Client  //Redis客户端
var RedisTemp *redis.Client  //Redis客户端

var RunConfig rdb.MapString     //本服务实时动态
var PublicDirPath rdb.MapString //公共文件夹路径

var ServerPort rdb.MapInt64       //所有服务监听的端口
var ServerHeartbeat rdb.MapInt64  //所有服务心跳时间
var ServerGoroutine rdb.MapInt64  //所有服务协程数量
var ServerBuildTime rdb.MapString //所有服务编译时间

func init() {
	var err error
	MySQL, err = cfg.LinkMySQL(cfg.Config.MySQL.DefaultDB, cfg.Config.MySQL.LogModel, cfg.Config.MySQL.CloseAffair)
	if err != nil {
		time.Sleep(time.Second * 5)
		panic(err)
	}
	MySQLData, err = cfg.LinkMySQL(cfg.Config.MySQL.DataDB, cfg.Config.MySQL.LogModel, cfg.Config.MySQL.CloseAffair)
	if err != nil {
		time.Sleep(time.Second * 5)
		panic(err)
	}

	Redis, err = cfg.LinkRedis(cfg.Config.Redis.DefaultDB)
	if err != nil {
		time.Sleep(time.Second * 5)
		panic(err)
	}
	RedisToken, err = cfg.LinkRedis(cfg.Config.Redis.TokenDB)
	if err != nil {
		time.Sleep(time.Second * 5)
		panic(err)
	}
	RedisData, err = cfg.LinkRedis(cfg.Config.Redis.DataDB)
	if err != nil {
		time.Sleep(time.Second * 5)
		panic(err)
	}
	RedisTemp, err = cfg.LinkRedis(cfg.Config.Redis.TempDB)
	if err != nil {
		time.Sleep(time.Second * 5)
		panic(err)
	}

	RunConfig = rdb.NewMapString(Redis, fmt.Sprint(cfg.Config.Main.ProjectName, "_Config"))
	PublicDirPath = rdb.NewMapString(Redis, "PublicDirPath")

	ServerPort = rdb.NewMapInt64(Redis, "ServerPort")
	ServerHeartbeat = rdb.NewMapInt64(Redis, "ServerHeartbeat")
	ServerGoroutine = rdb.NewMapInt64(Redis, "ServerGoroutine")
	ServerBuildTime = rdb.NewMapString(Redis, "ServerBuildTime")

	setPublicDirPath()
	go autoUpdateStatus()
}

// autoUpdateStatus 自动更新go总协程量
func autoUpdateStatus() {
	ticker := time.NewTicker(time.Second * 10)
	for {
		t := <-ticker.C
		memoryInfo, _ := mem.VirtualMemory()
		var diskInfo *disk.UsageStat
		if runtime.GOOS == "windows" {
			diskInfo, _ = disk.Usage("C:/")
		} else {
			diskInfo, _ = disk.Usage("/")
		}
		var v = make(map[string]string)
		v["总协程量："] = fmt.Sprint(runtime.NumGoroutine())
		v["当前程序的编译时间为："] = cfg.BuildTime
		v["程序心跳时间："] = fmt.Sprint(t.Unix())
		v["监听的端口："] = fmt.Sprint(cfg.Config.Main.Port)
		v["内存容量："] = fmt.Sprint(std.BitAutoString(int64(memoryInfo.Total)))
		v["内存已用："] = fmt.Sprint(std.BitAutoString(int64(memoryInfo.Used)))
		v["内存已用%："] = fmt.Sprint(int64(memoryInfo.UsedPercent))
		v["内存剩余："] = fmt.Sprint(std.BitAutoString(int64(memoryInfo.Total - memoryInfo.Used)))
		v["硬盘容量："] = fmt.Sprint(std.BitAutoString(int64(diskInfo.Total)))
		v["硬盘已用："] = fmt.Sprint(std.BitAutoString(int64(diskInfo.Used)))
		v["硬盘已用%："] = fmt.Sprint(int64(diskInfo.UsedPercent))
		v["硬盘剩余："] = fmt.Sprint(std.BitAutoString(int64(diskInfo.Total - diskInfo.Used)))
		_ = RunConfig.Sets(v)

		_ = ServerPort.Set("Admin后台", int64(cfg.Config.Main.Port))
		_ = ServerHeartbeat.Set("Admin后台", time.Now().Unix())
		_ = ServerGoroutine.Set("Admin后台", int64(runtime.NumGoroutine()))
		_ = ServerBuildTime.Set("Admin后台", cfg.BuildTime)
	}
}

const (
	PublicDirKeyDownload     = "公共下载路径"
	PublicDirKeyDownloadTemp = "公共临时下载路径"
	PublicDirKeyUpload       = "公共上传路径"
	PublicDirKeyStatic       = "静态文件路径"
	AutoRemoveFileSecond     = 3600 //自动删除文件，删除多少秒以前的文件
)

func setPublicDirPath() {
	if runtime.GOOS == "windows" {
		_, err := PublicDirPath.Get(PublicDirKeyDownload)
		if err != nil {
			_ = PublicDirPath.Set(PublicDirKeyDownload, "D:/nginx/files/download")
		}
		_, err = PublicDirPath.Get(PublicDirKeyDownloadTemp)
		if err != nil {
			_ = PublicDirPath.Set(PublicDirKeyDownloadTemp, "D:/nginx/files/download_temp")
		}
		_, err = PublicDirPath.Get(PublicDirKeyUpload)
		if err != nil {
			_ = PublicDirPath.Set(PublicDirKeyUpload, "D:/nginx/files/upload")
		}
		_, err = PublicDirPath.Get(PublicDirKeyStatic)
		if err != nil {
			_ = PublicDirPath.Set(PublicDirKeyStatic, "D:/nginx/files/static")
		}
	} else {
		_, err := PublicDirPath.Get(PublicDirKeyDownload)
		if err != nil {
			_ = PublicDirPath.Set(PublicDirKeyDownload, "/root/nginx/download")
		}
		_, err = PublicDirPath.Get(PublicDirKeyDownloadTemp)
		if err != nil {
			_ = PublicDirPath.Set(PublicDirKeyDownloadTemp, "/root/nginx/download_temp")
		}
		_, err = PublicDirPath.Get(PublicDirKeyUpload)
		if err != nil {
			_ = PublicDirPath.Set(PublicDirKeyUpload, "/root/nginx/upload")
		}
		_, err = PublicDirPath.Get(PublicDirKeyStatic)
		if err != nil {
			_ = PublicDirPath.Set(PublicDirKeyStatic, "/root/nginx/static")
		}
	}

	dirPath, err := PublicDirPath.Get(PublicDirKeyDownload)
	if err == nil {
		_ = os.MkdirAll(dirPath, cfg.DefaultFileModel)
	}
	dirPath, err = PublicDirPath.Get(PublicDirKeyDownloadTemp)
	if err == nil {
		_ = os.MkdirAll(dirPath, cfg.DefaultFileModel)
	}
	dirPath, err = PublicDirPath.Get(PublicDirKeyUpload)
	if err == nil {
		_ = os.MkdirAll(dirPath, cfg.DefaultFileModel)
	}
	dirPath, err = PublicDirPath.Get(PublicDirKeyStatic)
	if err == nil {
		_ = os.MkdirAll(dirPath, cfg.DefaultFileModel)
	}

	rwheel.AppendToTimeWheel(&rwheel.TimeWheel{
		IntervalMilli: 10000,
		Ctx:           nil,
		Func:          autoClearFile,
		IsLoop:        true,
	}, false)
}

func autoClearFile(ctx context.Context) {
	dirPath, err := PublicDirPath.Get(PublicDirKeyDownloadTemp)
	if err == nil {
		removeFiles(dirPath, time.Now().Unix()-AutoRemoveFileSecond) //删除临时文件
	}
	dirPath, err = PublicDirPath.Get(PublicDirKeyUpload) //删除上传的文件
	if err == nil {
		removeFiles(dirPath, time.Now().Unix()-AutoRemoveFileSecond)
	}
}

func removeFiles(dirPath string, minTimeUnix int64) {
	filepathList, err := rtool.RangeDirFile(dirPath)
	if err != nil {
		return
	}
	for _, d := range filepathList {
		stat, err := os.Stat(d)
		if err == nil {
			if stat.ModTime().Unix() < minTimeUnix {
				if stat.IsDir() {
					rlog.Info("自动删除文件夹：", os.RemoveAll(d))
				} else {
					rlog.Info("自动删除文件：", os.Remove(d))
				}
			}
		}
	}
}
